package no.hig.ekm.ircclient;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyVetoException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.HashMap;
import java.util.concurrent.Delayed;

import javax.swing.AbstractAction;
import javax.swing.JInternalFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import jerklib.Profile;

/**
 * Class administering a tree, which is an complete overview of
 * which servers and channels we are connected to. 
 * 
 * @author eigil
 */
public class MainChannelTree implements TreeSelectionListener{
	private static ResourceBundle messages;
	
	public static DefaultMutableTreeNode root;
	public HashMap<String, ChannelWindow> servers;
	public static JTree tree;
	public static ArrayList<DefaultMutableTreeNode> unseenNodes = new ArrayList<DefaultMutableTreeNode>(); 
	private JPopupMenu popupMenu;
	private JMenuItem menuItem;
	private ChannelList chlist;
	private String selectedServer = null;
	public static ChannelList serv = null;

	/**
	 * Initializes the tree
	 */
	public MainChannelTree() {
		root = new DefaultMutableTreeNode(messages.getString("mainChannelThreeRootNode"));
		tree = new JTree(root);
		tree.getSelectionModel().setSelectionMode(
				TreeSelectionModel.SINGLE_TREE_SELECTION);
		// tree.setCellRenderer(renderer);
		tree.addTreeSelectionListener(this);
		tree.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent me) {
				doMouseClicked(me);
			}
		});
		tree.setCellRenderer(new DefaultTreeCellRenderer() {
			/**
			 * Custom cell renderer for the JTree
			 */
			private static final long serialVersionUID = 1L;

			public Component getTreeCellRendererComponent(JTree pTree,
					Object pValue, boolean pIsSelected, boolean pIsExpanded,
					boolean pIsLeaf, int pRow, boolean pHasFocus) {
				DefaultMutableTreeNode node = (DefaultMutableTreeNode) pValue;
				super.getTreeCellRendererComponent(pTree, pValue, pIsSelected,
						pIsExpanded, pIsLeaf, pRow, pHasFocus);
				
						if (find(node)) {
							setBackgroundNonSelectionColor(Color.red);
						} else {
							setBackgroundNonSelectionColor(Color.white); //no tool tip
			            }

				return (this);
			}
		});
		
		AbstractAction op = new AbstractAction() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				serv = new ChannelList(selectedServer);
				Main.win.md.add (serv);
				serv.toFront();
				try {
					serv.setSelected(true);
				} catch (PropertyVetoException e) {
					e.printStackTrace();
				}
				System.err.println("Test");
			}
		};
		servers = new HashMap<String, ChannelWindow>();
		//Creating items in the popup menu and adds the actionlisteners
		
				popupMenu = new JPopupMenu();
				menuItem = popupMenu.add("List channels");
				menuItem.addActionListener(op);
	}

	
	/**
	 * Function looks for specified node in the 'unseenNodes' array
	 * 
	 * @param node
	 * @return True or false
	 */
	public static boolean find(DefaultMutableTreeNode node) {
		if (!unseenNodes.isEmpty()) {
			for (DefaultMutableTreeNode nd : unseenNodes) {
				if (node == nd)
					return true;
			}
		}
		return false;
	}
	
	/**
	 * Adds a server to the channel. The servers are all placed
	 * right under the root.
	 * 
	 * @param cw - ChannelWindow representing a server
	 */
	public void addServer(ChannelWindow cw) {
		servers.put(cw.toString(), cw);
		root.add(new DefaultMutableTreeNode(cw));
		((DefaultTreeModel) tree.getModel()).reload();
		expandTree();
	}
	
	/**
	 * Adds a channel into the server tree. The channel is
	 * placed as a child under the correct server-node.
	 * 
	 * @param serverName - Name of the server
	 * @param cw - ChannelWindow for the channel
	 */
	public void addChannel(String serverName, ChannelWindow cw) {
		DefaultTreeModel model;
		DefaultMutableTreeNode root, node;
		ChannelWindow server;
		
		model = (DefaultTreeModel) tree.getModel();
		root = (DefaultMutableTreeNode) model.getRoot();
		node = (DefaultMutableTreeNode) root.getFirstChild();
		do {
			server = (ChannelWindow) node.getUserObject();
			if(server.toString().equals(serverName)) {
				DefaultMutableTreeNode nd = new DefaultMutableTreeNode(cw);
				nd.setAllowsChildren(false);
				node.add(nd);
				break;
			}
		} while((node = node.getNextSibling()) != null);
		
		model.reload();
		expandTree();
	}
	/**
	 * Locates and removes a channel from the channel-tree. If the ChannwlWindow 
	 * is a server status window, the session is disconnected, and all the channels
	 * which belongs to this Session is parted.
	 * 
	 * @param serverName - Name of the server
	 * @param cw - ChannelWindow for the channel
	 */
	public void removeChannel(String serverName, ChannelWindow cw) {
		DefaultTreeModel model;
		DefaultMutableTreeNode root, node, child;
		ChannelWindow server, channel;
		
		model = (DefaultTreeModel) tree.getModel();
		root = (DefaultMutableTreeNode) model.getRoot();
		if (root.getChildCount()>0) {
			node = (DefaultMutableTreeNode) root.getFirstChild();
			
			//For each child node, do:
			do {
				server = (ChannelWindow) node.getUserObject();
				if (server != null){
					if(server.toString().equals(serverName)) {
						//Check whether the server has any channels open
						if (node.getChildCount()>0) {
							child = (DefaultMutableTreeNode) node.getFirstChild();
							do {
								channel = (ChannelWindow) child.getUserObject();
								if(channel == cw) {
									node.remove(child);
								}
							} while ((child = child.getNextSibling()) != null);
						}
					}
					//If we are quitting from server, remove all channels
					if (cw.getTitle().equals(serverName)) {
						node.removeAllChildren();
						node.removeFromParent();
					}
				}
			} while((node = node.getNextSibling()) != null);
		}
		model.reload();
		expandTree();
	}
	
	/**
	 * Gives access to the root of the tree.
	 * 
	 * @return a reference to the tree.
	 */
	public JTree getTree() {
		return tree;
	}
	
	/**
	 * Method which returns an array containing all the names of the connected
	 * servers.
	 * 
	 * @return an Array, with all the servers.
	 */
	public String[] getServerNames() {
		DefaultMutableTreeNode child;
		ChannelWindow cw;
		int no_children = root.getChildCount();
		String[] keys = new String[no_children];
		
		for(int i = 0; i < no_children; i++) {
			child = (DefaultMutableTreeNode) root.getChildAt(i);
			cw = (ChannelWindow) child.getUserObject();
			keys[i] = cw.toString();
		}
		
		return keys;
	}

	@Override
	public void valueChanged(TreeSelectionEvent e) {
		
	}

	/**
	 * Expands all the rows from the root tree
	 */
	public void expandTree() {
		if (tree.getRowCount() > 1) {
			for (int i = 0; i < tree.getRowCount(); i++) {
				tree.expandRow(i);
			}
		}
	}

	/**
	 * Looks for specific server and/or channel in the JTree
	 * If found - return it's TreePath
	 * 
	 * @param root
	 * @param server
	 * @param channel
	 * @return TreePath
	 */
	private static TreePath find(DefaultMutableTreeNode root, String server,
			String channel) {
		@SuppressWarnings("unchecked")
		Enumeration<DefaultMutableTreeNode> e = root.depthFirstEnumeration();
		while (e.hasMoreElements()) {
			DefaultMutableTreeNode node = e.nextElement();
			// If we have both server and channel to look for
			if (!channel.isEmpty()) {
				// If we presumably found the channel..
				if (node.toString().equalsIgnoreCase(channel)) {
					// Check if it belongs to the right server
					if (node.getParent().toString().equalsIgnoreCase(server))
						return new TreePath(node.getPath());
				}
			} else {
				// Otherwise look only for the server
				if (node.toString().equalsIgnoreCase(server)) {
					return new TreePath(node.getPath());
				}
			}
		}
		return null;
	}

	/**
	 * Looks for a server and/or channel
	 * If found, selects it in the JTree
	 * Also checks if the currently selected node (channel or server) is 
	 *   in the 'unseenNodes' array. If so, remove it.
	 * @param server
	 * @param channel
	 */
	public static void findNode(String server, String channel) {
		TreePath tp = find(root, server, channel);
		if (tp != null) {
			//System.err.println("Finding and selecting NODE!");
			tree.scrollPathToVisible(tp);
			tree.setSelectionPath(tp);
			DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode)tp.getLastPathComponent();
			if (unseenNodes.contains(dmtn)) {
				//System.err.println("Removing coloring from NODE!");
				unseenNodes.remove(dmtn);
			}
		}
	}

	/**
	 * Adds specified server and/or channel to the 'unseenNodes' array
	 * When repainting the tree, all nodes matching those in the array gets painted
	 * @param server
	 * @param channel
	 */
	public static void colorNode(String server, String channel) {
		TreePath tp = find(root, server, channel);
		if (tp != null) {
			//System.err.println("coloring NODE!");
			//Get the node from the treepath and add it
			DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode)tp.getLastPathComponent();
			if (!unseenNodes.contains(dmtn))
				unseenNodes.add(dmtn);
			tree.repaint();
		}
	}

	/**
	 * Mouse event when clicked on the JTree
	 * Handles channel/server window state changing
	 * @param me
	 */
	void doMouseClicked(MouseEvent me) {
		if (me.getButton() == MouseEvent.BUTTON1) { // if the left mouse button
													// was clicked
			TreePath tp = tree.getPathForLocation(me.getX(), me.getY());
			if (tp != null) {
				DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp
						.getLastPathComponent();
				if (node == null)
					return;

				Object o = node.getUserObject();
				if (o instanceof ChannelWindow) {
					int j = 0;
					ChannelWindow cw = (ChannelWindow) o;
					JInternalFrame[] frames = Main.win.md.getAllFrames();

					for (int i = 0; i < frames.length; i++) {
						if (frames[i] == cw) {
							j = 1;
							break;
						}
					}
					if (j == 0)
						Main.win.md.add(cw);

					// Window state handler
					if (cw.isIcon()) {
						try {
							cw.setIcon(false);
						} catch (PropertyVetoException e) {
							e.printStackTrace();
						}
						cw.toFront();
						// TODO set focus to the command field
						// cw.setVisible(true);
					} else if (!cw.isSelected()) {
						cw.toFront();
						try {
							cw.setSelected(true);
						} catch (PropertyVetoException e) {
							e.printStackTrace();
						}
						// cw.setVisible(true);
					} else if (!cw.isIcon() && cw.isSelected()) {
						try {
							cw.setIcon(true);
						} catch (PropertyVetoException e) {
							e.printStackTrace();
						}
					}
					try {
						cw.setSelected(true);
					} catch (PropertyVetoException pve) {
					}
				}
			}
		} else if (me.getButton() == MouseEvent.BUTTON3) { // if the right mouse button
			//tree.setSelectedIndex(tree.locate(me.getX(),me.getY());	//select the item
			TreePath tp = tree.getPathForLocation(me.getX(), me.getY());
			if (tp != null) {
				DefaultMutableTreeNode node = (DefaultMutableTreeNode) tp
						.getLastPathComponent();
				if (node == null)
					return;
			tree.setSelectionPath(tp);
			if (!node.isRoot() && node.getAllowsChildren()) {
				System.err.println("Is Server! ");
				popupMenu.show(tree, me.getX(), me.getY());
				selectedServer = node.toString();
			} else {
				System.err.println("is NOT a Server");
				selectedServer = null;
			}
			}
		}
	}

	/**
	 * Sets the ResourceBundle used for internationalization.
	 * 
	 * @param m - ResourceBundle
	 */
	public static void setMessages(ResourceBundle m){
		messages = m;
	}
}
